/* Yet Another Experimental Software Renderer
 *
 * Copyright (c) 2010 Pawel "Hery" Dziepak
 */

#include "tokenizer.h"

using namespace yasl;

const char tokenizer::whitespaces[] = { ' ', '\n', '\r', '\t', };
const char tokenizer::operator_chars[] = { 
	'{', '}', '%', '^', '&', '*', '(', ')', '-', '+', '=', '[', ']', ';', ':', '\'', '\"', '\\', '|',
	',', '<', '.', '>', '/', '?',
};

const char *tokenizer::data_types[] = { 
	"vertex", "texel", "float", "int", "vec2", "vec4", "color",
};

const char *tokenizer::data_semantics[] = { 
	"position", "hposition", "normal", "texcoord0", "texcoord1", "color0", "color1",
};

const char *tokenizer::keywords[] = { 
	"return", "if", "while", "for", "const", "do", "struct",
};


const char *tokenizer::operators[] = { 
	"%", "^", "*", "-", "+", "=", "/",
};


tokenizer::token_type tokenizer::get_type() const {
	/* token_data_semantics */
	if (is_one_of(buffer.c_str(), data_semantics, 7))
		return token_data_semantics;

	/* token_data_type */
	if (is_one_of(buffer.c_str(), data_types, 7))
		return token_data_type;

	/* token_keyword */
	if (is_one_of(buffer.c_str(), keywords, 7))
		return token_keyword;

	/* token_number */
	bool passed = true;
	for (int i = 0; i < buffer.size(); i++)
		if (!isdigit(buffer[i]))
			passed = false;
	if (passed)
		return token_number;

	/* token_name */
	passed = true;
	for (int i = 0; i < buffer.size(); i++)
		if (!isalpha(buffer[i]) && buffer[i] != '_')
			passed = false;
	if (passed)
		return token_name;

	/* token_operator */
	if (is_one_of(buffer.c_str(), operators, 7))
		return token_operator;

	/* token_symbol */
	passed = true;
	for (int i = 0; i < buffer.size(); i++)
		if (!is_in(buffer[i], operator_chars, sizeof(operator_chars)))
			passed = false;
	if (passed)
		return token_symbol;


	return token_unknown;
}

const std::string &tokenizer::get_string() const {
	return buffer;
}

bool tokenizer::is_in(char x, const char *chars, int size) {
	for (int i = 0; i < size; i++)
		if (chars[i] == x)
			return true;

	return false;
}

bool tokenizer::is_one_of(const char *x, const char **strings, int size) {
	for (int i = 0; i < size; i++)
		if (!strcmp(x, strings[i]))
			return true;

	return false;
}

int tokenizer::find_in(const char *x, const char **strings, int size) {
	for (int i = 0; i < size; i++)
		if (!strcmp(x, strings[i]))
			return i;

	return -1;
}

bool tokenizer::next() {
	buffer.clear();

	while (is_in(reader->get(), whitespaces, sizeof(whitespaces)))
		reader->next();

	if (!reader->get())
		return false;

	while (!is_in(reader->get(), whitespaces, sizeof(whitespaces)) &&
		!is_in(reader->get(), operator_chars, sizeof(operator_chars)) &&
		reader->get()) {
		buffer.push_back(reader->get());
		reader->next();
	}
	
	if (buffer.size() != 0)
		return true;

	while (!is_in(reader->get(), whitespaces, sizeof(whitespaces)) &&
		is_in(reader->get(), operator_chars, sizeof(operator_chars)) &&
		reader->get()) {
		buffer.push_back(reader->get());
		reader->next();
	}

	return true;
}